package jp.co.nes.awssupport.common.utility;

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;

import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.regions.Regions;

import jp.co.nes.awssupport.common.base.CommonException;

/**
 * 各AwsHelperの基底クラス。<br>
 */
public abstract class AwsHelperBase {

    /** PropertiesUtilからAWS接続に関する情報を取得する際に使用するキー名 */
    protected enum Key {
        /** アクセスキーを取得する際に使用するキー名 */
        ACCESS_KEY,

        /** シークレットキーを取得する際に使用するキー名 */
        SECRET_KEY,

        /** プロキシサーバのホストを取得する際に使用するキー名 */
        PROXY_HOST,

        /** プロキシサーバのポートを取得する際に使用するキー名 */
        PROXY_PORT,

        /**
         * プロキシを使用しないホスト(複数設定する場合は「|」で区切る、最初 or 最後にワイルドカード「*」の使用が可能)を
         * 取得する際に使用するキー名
         */
        NON_PROXY_HOSTS;

        /**
         * PropertiesUtilからAWS接続に関する情報を取得する際に使用するキー名を返却します。
         *
         * @return PropertiesUtilからAWS接続に関する情報を取得する際に使用するキー名
         */
        public String getKeyName() {
            boolean isLowerCase = false;
            StringBuilder builder = new StringBuilder();
            String name = name();
            for (int i = 0; i < name.length(); i++) {
                char ch = name.charAt(i);
                if (ch == '_') {
                    isLowerCase = false;
                } else {
                    if (isLowerCase) {
                        builder.append(Character.toLowerCase(ch));
                    } else {
                        builder.append(ch);
                        isLowerCase = true;
                    }
                }
            }
            return builder.toString();
        }
    }

    /**
     * デフォルトコンストラクタです。
     */
    public AwsHelperBase() {
        Regions regions = getRegions();
        String[] keys = new String[] {
                Key.ACCESS_KEY.getKeyName(),
                Key.SECRET_KEY.getKeyName(),
                Key.PROXY_HOST.getKeyName(),
                Key.PROXY_PORT.getKeyName(),
                Key.NON_PROXY_HOSTS.getKeyName()
        };
        Map<String, String> awsConnectInfoMap = createAwsConnectInfoMap(keys);
        String accessKey = awsConnectInfoMap.get(Key.ACCESS_KEY.getKeyName());
        String secretKey = awsConnectInfoMap.get(Key.SECRET_KEY.getKeyName());
        String proxyHost = awsConnectInfoMap.get(Key.PROXY_HOST.getKeyName());
        String proxyPort = awsConnectInfoMap.get(Key.PROXY_PORT.getKeyName());
        String nonProxyHosts = awsConnectInfoMap.get(Key.NON_PROXY_HOSTS.getKeyName());
        init(regions, createAWSCredentials(accessKey, secretKey),
                createClientConfiguration(proxyHost, proxyPort, nonProxyHosts));
    }

    /**
     * Regionsを引数にもつコンストラクタです。
     *
     * @param regions Regions
     */
    public AwsHelperBase(Regions regions) {
        String[] keys = new String[] {
                Key.ACCESS_KEY.getKeyName(),
                Key.SECRET_KEY.getKeyName(),
                Key.PROXY_HOST.getKeyName(),
                Key.PROXY_PORT.getKeyName(),
                Key.NON_PROXY_HOSTS.getKeyName()
        };
        Map<String, String> awsConnectInfoMap = createAwsConnectInfoMap(keys);
        String accessKey = awsConnectInfoMap.get(Key.ACCESS_KEY.getKeyName());
        String secretKey = awsConnectInfoMap.get(Key.SECRET_KEY.getKeyName());
        String proxyHost = awsConnectInfoMap.get(Key.PROXY_HOST.getKeyName());
        String proxyPort = awsConnectInfoMap.get(Key.PROXY_PORT.getKeyName());
        String nonProxyHosts = awsConnectInfoMap.get(Key.NON_PROXY_HOSTS.getKeyName());
        init(regions, createAWSCredentials(accessKey, secretKey),
                createClientConfiguration(proxyHost, proxyPort, nonProxyHosts));
    }

    /**
     * ClientConfigurationを引数にもつコンストラクタです。
     *
     * @param config ClientConfiguration
     */
    public AwsHelperBase(ClientConfiguration config) {
        Regions regions = getRegions();
        String[] keys = new String[] {
                Key.ACCESS_KEY.getKeyName(),
                Key.SECRET_KEY.getKeyName()
        };
        Map<String, String> awsConnectInfoMap = createAwsConnectInfoMap(keys);
        String accessKey = awsConnectInfoMap.get(Key.ACCESS_KEY.getKeyName());
        String secretKey = awsConnectInfoMap.get(Key.SECRET_KEY.getKeyName());
        init(regions, createAWSCredentials(accessKey, secretKey), config);
    }

    /**
     * Regions、ClientConfigurationを引数にもつコンストラクタです。
     *
     * @param regions Regions
     * @param config ClientConfiguration
     */
    public AwsHelperBase(Regions regions, ClientConfiguration config) {
        String[] keys = new String[] {
                Key.ACCESS_KEY.getKeyName(),
                Key.SECRET_KEY.getKeyName()
        };
        Map<String, String> awsConnectInfoMap = createAwsConnectInfoMap(keys);
        String accessKey = awsConnectInfoMap.get(Key.ACCESS_KEY.getKeyName());
        String secretKey = awsConnectInfoMap.get(Key.SECRET_KEY.getKeyName());
        init(regions, createAWSCredentials(accessKey, secretKey), config);
    }

    /**
     * アクセスキー、シークレットキーを引数にもつコンストラクタです。
     *
     * @param accessKey アクセスキー
     * @param secretKey シークレットキー
     */
    public AwsHelperBase(String accessKey, String secretKey) {
        Regions regions = getRegions();
        String[] keys = new String[] {
                Key.PROXY_HOST.getKeyName(),
                Key.PROXY_PORT.getKeyName(),
                Key.NON_PROXY_HOSTS.getKeyName()
        };
        Map<String, String> awsConnectInfoMap = createAwsConnectInfoMap(keys);
        String proxyHost = awsConnectInfoMap.get(Key.PROXY_HOST.getKeyName());
        String proxyPort = awsConnectInfoMap.get(Key.PROXY_PORT.getKeyName());
        String nonProxyHosts = awsConnectInfoMap.get(Key.NON_PROXY_HOSTS.getKeyName());
        init(regions, createAWSCredentials(accessKey, secretKey),
                createClientConfiguration(proxyHost, proxyPort, nonProxyHosts));
    }

    /**
     * Regions、アクセスキー、シークレットキーを引数にもつコンストラクタです。
     *
     * @param regions Regions
     * @param accessKey アクセスキー
     * @param secretKey シークレットキー
     */
    public AwsHelperBase(Regions regions, String accessKey, String secretKey) {
        String[] keys = new String[] {
                Key.PROXY_HOST.getKeyName(),
                Key.PROXY_PORT.getKeyName(),
                Key.NON_PROXY_HOSTS.getKeyName()
        };
        Map<String, String> awsConnectInfoMap = createAwsConnectInfoMap(keys);
        String proxyHost = awsConnectInfoMap.get(Key.PROXY_HOST.getKeyName());
        String proxyPort = awsConnectInfoMap.get(Key.PROXY_PORT.getKeyName());
        String nonProxyHosts = awsConnectInfoMap.get(Key.NON_PROXY_HOSTS.getKeyName());
        init(regions, createAWSCredentials(accessKey, secretKey),
                createClientConfiguration(proxyHost, proxyPort, nonProxyHosts));
    }

    /**
     * ClientConfiguration、アクセスキー、シークレットキーを引数にもつコンストラクタです。
     *
     * @param accessKey アクセスキー
     * @param secretKey シークレットキー
     * @param config ClientConfiguration
     */
    public AwsHelperBase(String accessKey, String secretKey, ClientConfiguration config) {
        Regions regions = getRegions();
        init(regions, createAWSCredentials(accessKey, secretKey), config);
    }

    /**
     * Regions、アクセスキー、シークレットキー、ClientConfigurationを引数にもつコンストラクタです。
     *
     * @param regions Regions
     * @param accessKey アクセスキー
     * @param secretKey シークレットキー
     * @param config ClientConfiguration
     */
    public AwsHelperBase(Regions regions, String accessKey, String secretKey, ClientConfiguration config) {
        init(regions, createAWSCredentials(accessKey, secretKey), config);
    }

    /**
     * アクセスキー、シークレットキー、プロキシサーバのホスト、プロキシサーバのポートを引数にもつコンストラクタです。
     *
     * @param accessKey アクセスキー
     * @param secretKey シークレットキー
     * @param proxyHost プロキシサーバのホスト
     * @param proxyPort プロキシサーバのポート
     * @param nonProxyHosts プロキシを使用しないホスト(複数設定する場合は「|」で区切る、最初 or 最後にワイルドカード「*」の使用が可能)
     */
    public AwsHelperBase(String accessKey, String secretKey, String proxyHost, String proxyPort, String nonProxyHosts) {
        Regions regions = getRegions();
        init(regions, createAWSCredentials(accessKey, secretKey),
                createClientConfiguration(proxyHost, proxyPort, nonProxyHosts));
    }

    /**
     * Regions、アクセスキー、シークレットキー、プロキシサーバのホスト、プロキシサーバのポートを引数にもつコンストラクタです。
     *
     * @param regions Regions
     * @param accessKey アクセスキー
     * @param secretKey シークレットキー
     * @param proxyHost プロキシサーバのホスト
     * @param proxyPort プロキシサーバのポート
     * @param nonProxyHosts プロキシを使用しないホスト(複数設定する場合は「|」で区切る、最初 or 最後にワイルドカード「*」の使用が可能)
     */
    public AwsHelperBase(Regions regions, String accessKey, String secretKey, String proxyHost, String proxyPort,
            String nonProxyHosts) {
        init(regions, createAWSCredentials(accessKey, secretKey),
                createClientConfiguration(proxyHost, proxyPort, nonProxyHosts));
    }

    /**
     * AWSCredentialsを生成します。
     *
     * @param accessKey アクセスキー
     * @param secretKey シークレットキー
     * @return AWSCredentials
     */
    protected AWSCredentials createAWSCredentials(String accessKey, String secretKey) {
        String aKey = null;
        String sKey = null;
        if (StringUtils.isEmpty(accessKey)) {
            if (!StringUtils.isEmpty(secretKey)) {
                throw new CommonException("access key is null or empty, secret key is not null and empty");
            }
        } else {
            if (StringUtils.isEmpty(secretKey)) {
                throw new CommonException("access key is not null and empty, secret key is null or empty");
            } else {
                aKey = accessKey;
                sKey = secretKey;
            }
        }
        AWSCredentials credentials = null;
        if (aKey != null && sKey != null) {
            credentials = new BasicAWSCredentials(accessKey, secretKey);
        }
        return credentials;
    }

    /**
     * ClientConfigurationを生成します。
     *
     * @param proxyHost プロキシサーバのホスト
     * @param proxyPort プロキシサーバのポート
     * @param nonProxyHosts プロキシを使用しないホスト(複数設定する場合は「|」で区切る、最初 or 最後にワイルドカード「*」の使用が可能)
     * @return ClientConfiguration
     */
    protected ClientConfiguration createClientConfiguration(String proxyHost, String proxyPort, String nonProxyHosts) {
        String pHost = null;
        String pPort = null;
        String pNhosts = null;
        if (StringUtils.isEmpty(proxyHost)) {
            if (!StringUtils.isEmpty(proxyPort) || !StringUtils.isEmpty(nonProxyHosts)) {
                throw new CommonException(
                        "proxy host is null or empty, proxy port and non proxy hosts is not null and empty");
            }
        } else {
            if (StringUtils.isEmpty(proxyPort)) {
                throw new CommonException("proxy host is not null and empty, proxy port is null or empty");
            } else {
                pHost = proxyHost;
                pPort = proxyPort;
                pNhosts = nonProxyHosts;
            }
        }
        ClientConfiguration config = null;
        if (!StringUtils.isEmpty(pHost)) {
            config = new ClientConfiguration();
            config.setProxyHost(pHost);
            config.setProxyPort(Integer.parseInt(pPort));
            if (!StringUtils.isEmpty(pNhosts)) {
                config.setNonProxyHosts(pNhosts);
            }
        }
        return config;
    }

    /**
     * PropertiesUtilからAWS接続に関する情報を取得し、マップに設定して、返却します。
     *
     * @param keys PropertiesUtilからAWS接続に関する情報を取得する際に使用するキー名の配列
     * @return AWS接続に関する情報を設定したマップ
     */
    protected Map<String, String> createAwsConnectInfoMap(String[] keys) {
        Map<String, String> awsConnectInfoMap = new HashMap<>();
        for (String key : keys) {
            String value = PropertiesUtil.getProperty(key);
            if (!StringUtils.isEmpty(value)) {
                awsConnectInfoMap.put(key, value);
            }
        }
        return awsConnectInfoMap;
    }

    /**
     * 初期処理です。
     *
     * @param regions Regions
     * @param credentials AWSCredentials
     * @param config ClientConfiguration
     */
    protected abstract void init(Regions regions, AWSCredentials credentials, ClientConfiguration config);

    /**
     * Regionsを返却します。
     *
     * @return Regions
     */
    protected Regions getRegions() {
        Regions result = null;
        String regionName = PropertiesUtil.getProperty("RegionName");
        if (regionName == null) {
            result = Regions.AP_NORTHEAST_1;
        } else {
            result = Regions.fromName(regionName);
        }
        return result;
    }
}
